//===----------------------------------------------------------------------===//
//
//                         CMU-DB Project (15-445/645)
//                         ***DO NO SHARE PUBLICLY***
//
// Identification: src/page/b_plus_tree_internal_page.cpp
//
// Copyright (c) 2018, Carnegie Mellon University Database Group
//
//===----------------------------------------------------------------------===//

#include <iostream>
#include <sstream>

#include "common/exception.h"
#include "storage/page/b_plus_tree_internal_page.h"

namespace bustub {
/*****************************************************************************
 * HELPER METHODS AND UTILITIES
 *****************************************************************************/
/*
 * Init method after creating a new internal page
 * Including set page type, set current size, and set max page size
 */
INDEX_TEMPLATE_ARGUMENTS
void B_PLUS_TREE_INTERNAL_PAGE_TYPE::Init(int max_size) {
  SetPageType(IndexPageType::INTERNAL_PAGE);
  SetSize(0);
  SetMaxSize(max_size);
  // array_ = reinterpret_cast<MappingType *>(&array_[0]);
}
/*
 * Helper method to get/set the key associated with input "index"(a.k.a
 * array offset)
 */
INDEX_TEMPLATE_ARGUMENTS
auto B_PLUS_TREE_INTERNAL_PAGE_TYPE::KeyAt(int index) const -> KeyType {
  // replace with your own code
  return array_[index].first;
}

INDEX_TEMPLATE_ARGUMENTS
void B_PLUS_TREE_INTERNAL_PAGE_TYPE::SetKeyAt(int index, const KeyType &key) { array_[index].first = key; }

INDEX_TEMPLATE_ARGUMENTS
void B_PLUS_TREE_INTERNAL_PAGE_TYPE::SetValueAt(int index, const ValueType &value) { array_[index].second = value; }
/*
 * Helper method to get the value associated with input "index"(a.k.a array
 * offset)
 */
INDEX_TEMPLATE_ARGUMENTS
auto B_PLUS_TREE_INTERNAL_PAGE_TYPE::ValueAt(int index) const -> ValueType {
  // replace with your own code
  return array_[index].second;
}

INDEX_TEMPLATE_ARGUMENTS
auto B_PLUS_TREE_INTERNAL_PAGE_TYPE::FindGivenKey(const KeyType &key, KeyComparator cmp) -> int {
  for (int index = 1; index < GetSize(); index++) {
    if (cmp(key, KeyAt(index)) == 0) {
      return index;
    }
  }
  return -1;
}

INDEX_TEMPLATE_ARGUMENTS
auto B_PLUS_TREE_INTERNAL_PAGE_TYPE::RemovePage(const KeyType &key, page_id_t removed_page, KeyComparator cmp) -> bool {
  if (cmp(key, KeyAt(1)) < 0 && removed_page == ValueAt(0)) {
    // we need to remove page zero
    int index = 0;
    for (; index < GetSize() - 1; index++) {
      SetKeyAt(index, KeyAt(index + 1));
      SetValueAt(index, ValueAt(index + 1));
    }
    SetSize(GetSize() - 1);
  } else {
    int index = 1;
    for (; index < GetSize(); index++) {
      if (cmp(key, KeyAt(index)) == 0 && ValueAt(index) == removed_page) {
        break;
      }
    }
    for (; index < GetSize() - 1; index++) {
      SetKeyAt(index, KeyAt(index + 1));
      SetValueAt(index, ValueAt(index + 1));
    }
    SetSize(GetSize() - 1);
  }
  return true;
}

INDEX_TEMPLATE_ARGUMENTS
void B_PLUS_TREE_INTERNAL_PAGE_TYPE::StoleFromLeftSibling(
    BPlusTreeInternalPage<KeyType, ValueType, KeyComparator> *internal, KeyComparator cmp, const KeyType &old_key) {
  for (int index = internal->GetSize(); index > 0; index--) {
    internal->SetKeyAt(index, internal->KeyAt(index - 1));
    internal->SetValueAt(index, internal->ValueAt(index - 1));
  }
  internal->SetKeyAt(0, KeyAt(GetSize() - 1));
  internal->SetValueAt(0, ValueAt(GetSize() - 1));
  SetSize(GetSize() - 1);
  internal->SetSize(internal->GetSize() + 1);
}

INDEX_TEMPLATE_ARGUMENTS
void B_PLUS_TREE_INTERNAL_PAGE_TYPE::StoleFromRightSibling(
    BPlusTreeInternalPage<KeyType, ValueType, KeyComparator> *internal, KeyComparator cmp, const KeyType &old_key) {
  internal->SetKeyAt(internal->GetSize(), KeyAt(0));
  internal->SetValueAt(internal->GetSize(), ValueAt(0));
  for (int index = 0; index < GetSize() - 1; index++) {
    SetKeyAt(index, KeyAt(index + 1));
    SetValueAt(index, ValueAt(index + 1));
  }
  SetSize(GetSize() - 1);
  internal->SetSize(internal->GetSize() + 1);
}

INDEX_TEMPLATE_ARGUMENTS
void B_PLUS_TREE_INTERNAL_PAGE_TYPE::CombieWithRightSibling(
    BPlusTreeInternalPage<KeyType, ValueType, KeyComparator> *right_sibling, const KeyType &key) {
  SetKeyAt(GetSize(), right_sibling->KeyAt(0));
  SetValueAt(GetSize(), right_sibling->ValueAt(0));
  for (int i = 1; i < right_sibling->GetSize(); i++) {
    SetKeyAt(GetSize() + i, right_sibling->KeyAt(i));
    SetValueAt(GetSize() + i, right_sibling->ValueAt(i));
  }
  SetSize(GetSize() + right_sibling->GetSize());
  right_sibling->SetSize(0);
}

INDEX_TEMPLATE_ARGUMENTS
void B_PLUS_TREE_INTERNAL_PAGE_TYPE::CombieWithLeftSibling(
    BPlusTreeInternalPage<KeyType, ValueType, KeyComparator> *left_sibling, const KeyType &key) {
  int left_size = left_sibling->GetSize();
  for (int index = GetSize() - 1; index > 0; index--) {
    SetKeyAt(index + left_size, KeyAt(index));
    SetValueAt(index + left_size, ValueAt(index));
  }
  SetKeyAt(left_size, KeyAt(0));
  SetValueAt(left_size, ValueAt(0));
  for (int index = left_size - 1; index >= 0; index--) {
    SetKeyAt(index, left_sibling->KeyAt(index));
    SetValueAt(index, left_sibling->ValueAt(index));
  }
  SetSize(GetSize() + left_size);
  left_sibling->SetSize(0);
}

// valuetype for internalNode should be page id_t
template class BPlusTreeInternalPage<GenericKey<4>, page_id_t, GenericComparator<4>>;
template class BPlusTreeInternalPage<GenericKey<8>, page_id_t, GenericComparator<8>>;
template class BPlusTreeInternalPage<GenericKey<16>, page_id_t, GenericComparator<16>>;
template class BPlusTreeInternalPage<GenericKey<32>, page_id_t, GenericComparator<32>>;
template class BPlusTreeInternalPage<GenericKey<64>, page_id_t, GenericComparator<64>>;
}  // namespace bustub
